package org.test4j.mock.faking.util;

import java.util.concurrent.locks.ReentrantLock;

import static org.test4j.mock.faking.util.ReflectUtility.doThrow;
import static org.test4j.mock.faking.util.TypeUtility.PRIMITIVE_CLASS;

public final class ClassLoad {
    public static final ClassLoader CLASS_LOADER = SearchingClassLoader.combineLoadersOf(null);

    private ClassLoad() {
    }

    public static ClassLoader loadersOf(Class first, Class... types) {
        return SearchingClassLoader.combineLoadersOf(first, types);
    }

    /**
     * 查找class, initialize = false
     *
     * @param className
     * @param <T>
     * @return
     */
    public static <T> Class<T> loadClass(String className) {
        if (className == null) {
            return null;
        }
        if (PRIMITIVE_CLASS.containsKey(className)) {
            return PRIMITIVE_CLASS.get(className);
        }
        try {
            String clazzName = className.replace('/', '.');
            return (Class<T>) Class.forName(clazzName, false, CLASS_LOADER);
        } catch (LinkageError | ClassNotFoundException e) {
            return doThrow(e);
        }
    }


    private static final ReentrantLock LOCK = new ReentrantLock();

    /**
     * 是否在class loading阶段
     *
     * @return
     */
    public static boolean isDuringClassLoading() {
        if (LOCK.isHeldByCurrentThread()) {
            return true;
        }
        LOCK.lock();
        try {
            StackTrace st = new StackTrace(new Throwable());
            for (int index = 3; index < st.getDepth(); index++) {
                StackTraceElement ste = st.getElement(index);
                if ("ClassLoader.java".equals(ste.getFileName()) &&
                    "loadClass getResource loadLibrary".contains(ste.getMethodName())
                ) {
                    return true;
                }
            }
            return false;
        } finally {
            LOCK.unlock();
        }
    }


    /**
     * 类是否在classpath中
     *
     * @param typeName
     * @return
     */
    public static boolean isInClasspath(String typeName) {
        try {
            Class clazz = loadClass(typeName);
            return clazz != null;
        } catch (Throwable ignore) {
            return false;
        }
    }
}